package com.ggshily.util.io;

import java.io.DataInput;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UTFDataFormatException;

/**
 * based on http://www.javafaq.nu/java-example-code-1079.html
 * 
 * @author playfish_chg
 * 
 */
public class LittleEndianDataInputStream extends FilterInputStream implements
		DataInput
{

	public LittleEndianDataInputStream(InputStream in)
	{
		super(in);
	}

	@Override
	public boolean readBoolean() throws IOException
	{
		int bool = in.read();
		if (bool == -1)
			throw new EOFException();
		return (bool != 0);
	}

	@Override
	public byte readByte() throws IOException
	{
		int temp = in.read();
		if (temp == -1)
			throw new EOFException();
		return (byte) temp;
	}

	@Override
	public char readChar() throws IOException
	{
		int byte1 = in.read();
		int byte2 = in.read();
		if (byte2 == -1)
			throw new EOFException();
		return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
	}

	@Override
	public double readDouble() throws IOException
	{
		return Double.longBitsToDouble(this.readLong());
	}

	@Override
	public float readFloat() throws IOException
	{
		return Float.intBitsToFloat(this.readInt());
	}

	@Override
	public void readFully(byte[] b) throws IOException
	{
		readFully(b, 0, b.length);
	}

	@Override
	public void readFully(byte[] b, int off, int len) throws IOException
	{
		if (len < 0)
			throw new IndexOutOfBoundsException();
		int n = 0;
		while (n < len)
		{
			int count = in.read(b, off + n, len - n);
			if (count < 0)
				throw new EOFException();
			n += count;
		}
	}

	@Override
	public int readInt() throws IOException
	{
		int byte1 = in.read();
		int byte2 = in.read();
		int byte3 = in.read();
		int byte4 = in.read();
		if (byte4 == -1)
		{
			throw new EOFException();
		}
		return (byte4 << 24) + ((byte3 << 24) >>> 8) + ((byte2 << 24) >>> 16)
				+ ((byte1 << 24) >>> 24);
	}
	
	@Deprecated
	@Override
	public String readLine() throws IOException
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public long readLong() throws IOException
	{
		long byte1 = in.read();
		long byte2 = in.read();
		long byte3 = in.read();
		long byte4 = in.read();
		long byte5 = in.read();
		long byte6 = in.read();
		long byte7 = in.read();
		long byte8 = in.read();
		if (byte8 == -1)
		{
			throw new EOFException();
		}
		return (byte8 << 56) + ((byte7 << 56) >>> 8) + ((byte6 << 56) >>> 16)
				+ ((byte5 << 56) >>> 24) + ((byte4 << 56) >>> 32)
				+ ((byte3 << 56) >>> 40) + ((byte2 << 56) >>> 48)
				+ ((byte1 << 56) >>> 56);
	}

	@Override
	public short readShort() throws IOException
	{
		int byte1 = in.read();
		int byte2 = in.read();
		// only need to test last byte read
		// if byte1 is -1 so is byte2
		if (byte2 == -1)
			throw new EOFException();
		return (short) ((byte2 << 8) | (byte1 & 0xFF));
	}

	@Override
	public String readUTF() throws IOException
	{
		int byte1 = in.read();
		int byte2 = in.read();
		if (byte2 == -1)
			throw new EOFException();
		int numbytes = (byte1 << 8) + byte2;
		char result[] = new char[numbytes];
		int numread = 0;
		int numchars = 0;

		while (numread < numbytes)
		{

			int c1 = readUnsignedByte();

			// The first 4 bits of c1 determine how many bytes are in this char
			int test = c1 >> 4;
			if (test < 8)
			{ // one byte
				numread++;
				result[numchars++] = (char) c1;
			}
			else if (test == 12 || test == 13)
			{ // 2 bytes
				numread += 2;
				if (numread > numbytes)
					throw new UTFDataFormatException();
				int c2 = readUnsignedByte();
				if ((c2 & 0xC0) != 0x80)
					throw new UTFDataFormatException();
				result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
			}
			else if (test == 14)
			{ // three bytes
				numread += 3;
				if (numread > numbytes)
					throw new UTFDataFormatException();
				int c2 = readUnsignedByte();
				int c3 = readUnsignedByte();
				if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
				{
					throw new UTFDataFormatException();
				}
				result[numchars++] = (char) (((c1 & 0x0F) << 12)
						| ((c2 & 0x3F) << 6) | (c3 & 0x3F));
			}
			else
			{ // malformed
				throw new UTFDataFormatException();
			}

		} // end while

		return new String(result, 0, numchars);
	}

	@Override
	public int readUnsignedByte() throws IOException
	{
		int temp = in.read();
		if (temp == -1)
			throw new EOFException();
		return temp;
	}

	@Override
	public int readUnsignedShort() throws IOException
	{
		int byte1 = in.read();
		int byte2 = in.read();
		if (byte2 == -1)
			throw new EOFException();
		return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
	}

	@Override
	public int skipBytes(int n) throws IOException
	{
		for (int i = 0; i < n; i += (int) skip(n - i))
			;
		return n;
	}

}
